#pragma once

#define BOARD_ROW 15
#define BOARD_COL 15

#define CHESS_WHITE 1
#define CHESS_BLACK 2

typedef enum
{
    GAME_START,
    GAME_OVER
} room_statu;

#include <iostream>
#include <vector>

#include "online.hpp"
#include "data.hpp"
#include "util.hpp"
#include "log.hpp"

class room
{
private:
    uint64_t _room_id; // 房间id
    room_statu _statu; // 房间状态
    int _player_count; // 玩家数量

    uint64_t _white_id; // 白棋玩家
    uint64_t _black_id; // 黑棋玩家

    user_table *_tb_user; // 用户表，用于用户增加、删除、查找

    online_manager *_online_user; // 在线用户的管理

    std::vector<std::vector<int>> _board; // 棋盘

public:
    room(uint64_t room_id, user_table *tb_user, online_manager *online_user)
        : _room_id(room_id), _statu(GAME_START), _player_count(0), _tb_user(tb_user), _online_user(online_user),
          _board(BOARD_ROW, std::vector<int>(BOARD_COL, 0))
    {
        LOG_INF("%lu 房间创建成功！！", _room_id);
    }

    ~room()
    {
        LOG_INF("%lu 房间销毁成功！！", _room_id);
    }

    uint64_t id() { return _room_id; }

    room_statu statu() { return _statu; }

    int player_count() { return _player_count; }

    void add_white_user(uint64_t uid) { _white_id = uid;  _player_count++;}
    void add_black_user(uint64_t uid) { _black_id = uid; _player_count++;}

    uint64_t get_white_user() { return _white_id; }
    uint64_t get_black_user() { return _black_id; }

    // 下棋处理
    /*
    {
         "optype": "put_chess",     //当前请求的是下棋操作
         "room_id": 222,           //房间id
         "uid": 1,                //当前下棋操作是哪个用户发起的
         "row": 3,               //当前下棋的行号
         "col": 2               //当前下棋的列号
     }
    */

    // 走棋失败
    /*
    {
         "optype": "put_chess",     //当前请求的是下棋操作
         "result": false            //
         "reason": "失败的具体原因"
     }
    */

    // 一方掉线
    /*
    {
        "optype" : "put_chess",
        "result" : true,
        "reason" : "对方掉线，不战而胜！",
        "room_id" : 222,
        "uid" : 1,
        "row" : 3,
        "col" : 2,
        "winner" : 1
    }
   */

    bool five_chess(int chess_row, int chess_col, int row_off, int col_off, int color)
    {
        // row、col是下棋位置, off是偏移量, color是棋子颜色
        int count = 1;
        int search_row = chess_row + row_off;
        int search_col = chess_col + col_off;

        while (search_row >= 0 && search_row <= BOARD_ROW && search_col >= 0 && search_col <= BOARD_COL && _board[search_row][search_col] == color)
        {
            count++;
            search_row += row_off;
            search_col += col_off;
        }

        search_row = chess_row - row_off;
        search_col = chess_col - col_off;

        while (search_row >= 0 && search_row <= BOARD_ROW && search_col >= 0 && search_col <= BOARD_COL && _board[search_row][search_col] == color)
        {
            count++;
            search_row -= row_off;
            search_col -= col_off;
        }

        return (count >= 5);
    }

    uint64_t check_win(int chess_row, int chess_col, int cur_clolor)
    {
        // 从下棋位置的四个方向上检测是否出现了5个或以上相同颜色的棋子

        if (five_chess(chess_row, chess_col, 0, 1, cur_clolor) || five_chess(chess_row, chess_col, 1, 0, cur_clolor) || five_chess(chess_row, chess_col, -1, 1, cur_clolor) || five_chess(chess_row, chess_col, -1, -1, cur_clolor))
        {
            return cur_clolor == CHESS_WHITE ? _white_id : _black_id;
        }

        return 0;
    }

    Json::Value handle_chess(Json::Value &req)
    {

        Json::Value json_resp = req;
        // 当前请求的房间号，是否匹配
        uint64_t room_id = req["room_id"].asUInt64();

        if (room_id != _room_id)
        {
            json_resp["optype"] = "put_chess";
            json_resp["result"] = false;
            json_resp["reason"] = "房间号不匹配";
            LOG_ERR("房间号不匹配");
            return json_resp;
        }

        // 判断两个玩家是否都在线

        uint64_t cur_id = req["uid"].asInt64();

        if (_online_user->is_in_game_room(_white_id) == false)
        {
            json_resp["reason"] = "运气真好，对方掉线，不战而胜！";
            json_resp["result"] = true;
            json_resp["winner"] = (Json::UInt64)_black_id;

            return json_resp;
        }

        if (_online_user->is_in_game_room(_black_id) == false)
        {
            json_resp["reason"] = "运气真好，对方掉线，不战而胜！";
            json_resp["result"] = true;
            json_resp["winner"] = (Json::UInt64)_white_id;

            return json_resp;
        }

        // 获取走起位置，看是否合理
        int chess_row = req["row"].asInt();
        int chess_col = req["col"].asInt();

        if (_board[chess_row][chess_col] != 0)
        {
            json_resp["reason"] = "当前位置已经有其它棋子";
            json_resp["result"] = false;
            return json_resp;
        }

        int cur_clolor = cur_id == _white_id ? CHESS_WHITE : CHESS_BLACK;
        _board[chess_row][chess_col] = cur_clolor;
        // 判断当前这一步是否胜利

        uint64_t winner_id = check_win(chess_row, chess_col, cur_clolor);

        if (winner_id != 0)
        {
            json_resp["reason"] = "五星连珠，你赢啦";
        }
        json_resp["winner"] = (Json::UInt64)winner_id;
        json_resp["result"] = true;

        return json_resp;
    }

    // 聊天
    /*
    {
        "optype" : "chat",
        "room_id" : 222,
        "uid" : 1,
        "message": "请快速下棋"
    }
   */
    // 聊天处理
    Json::Value handle_chat(Json::Value &req)
    {
        // 检测房间是否一致

        Json::Value json_resp = req;
        // 当前请求的房间号，是否匹配
        uint64_t room_id = req["room_id"].asUInt64();

        if (room_id != _room_id)
        {
            json_resp["result"] = false;
            json_resp["reason"] = "房间号不匹配";
            return json_resp;
        }

        // 检测敏感词

        std::string message = req["message"].asString();

        size_t pos = message.find("真菜");

        if (pos != std::string::npos)
        {
            json_resp["result"] = false;
            json_resp["reason"] = "含有敏感词";

            return json_resp;
        }

        // 广播消息

        json_resp["result"] = true;
        return json_resp;
    }

    // 退出处理,单独的，不包含在处理调用中
    void handle_exit(uint64_t uid)
    {
        Json::Value json_resp;

        if (_statu == GAME_START)
        {
            uint64_t winner_id = (Json::UInt64)(winner_id==_white_id)? _white_id:_black_id;

            json_resp["optype"] = "put_chess";
            json_resp["result"] = true;
            json_resp["reason"] = "对方掉线，不战而胜";
            json_resp["room_id"] = (Json::UInt64)_room_id;
            json_resp["uid"] = (Json::UInt64)uid;

            //退出时，我们会设置下棋的位置为-1，-1
            json_resp["row"] = -1;
            json_resp["col"] = -1;
            json_resp["winner"] = (Json::UInt64)winner_id;

            uint64_t loser_id = winner_id == _white_id ? _black_id : _white_id;

            _tb_user->win(winner_id);
            _tb_user->lose(loser_id);
            _statu = GAME_OVER;
            boardcast(json_resp);
        }
        _player_count--;
        return;
    }

    // 总的请求函数，在函数内部，区分请求类型，根据不同的请求类型调用不同的处理函数
    Json::Value handle_request(Json::Value &req)
    {
        Json::Value json_resp;
        uint64_t room_id = req["room_id"].asUInt64();

        // 房间号匹配
        if (room_id != _room_id)
        {
            json_resp["optype"] = req["optype"].asString();
            json_resp["result"] = false;
            json_resp["reason"] = "房间号不匹配";
            return json_resp;
        }

        // 根据不同类型调用不同函数

        if (req["optype"].asString() == "put_chess")
        {
            json_resp = handle_chess(req);
            if (json_resp["winner"].asUInt64() != 0)
            {
                uint64_t winner_id = json_resp["winner"].asUInt64();
                uint64_t loser_id = winner_id == _white_id ? _black_id : _white_id;
                _tb_user->win(winner_id);
                _tb_user->lose(loser_id);
                _statu = GAME_OVER;
            }
        }
        else if (req["optype"].asString() == "chat")
        {
            json_resp = handle_chat(req);
        }
        else
        {
            json_resp["optype"] = req["optype"].asString();
            json_resp["result"] = false;
            json_resp["reason"] = "未知请求类型";
        }

        std::string body;

        ns_util::json_util::json_serialize(json_resp, &body);
        LOG_INF("房间-广播动作： %s", body.c_str());

        boardcast(json_resp);

        return json_resp;
    }

    // 广播处理,将指定的信息发送给所有玩家
    void boardcast(Json::Value &rsp)
    {
        std::string body;
        ns_util::json_util::json_serialize(rsp, &body);

        websocket_server::connection_ptr wconn = _online_user->get_conn_from_room(_white_id);

        if (wconn.get() != nullptr)
        {
            wconn->send(body);
        }
        else
        {
            LOG_INF("房间-白棋玩家获取连接失败");
        }

        websocket_server::connection_ptr bconn = _online_user->get_conn_from_room(_black_id);

        if (bconn.get() != nullptr)
        {
            bconn->send(body);
        }
        else
        {
            LOG_INF("房间-黑棋玩家获取连接失败");
        }

        return;
    }
};

using room_ptr = std::shared_ptr<room>;

class room_manager
{

private:
    uint64_t _next_rid;
    std::mutex _mutex;

    user_table *_tb_user;
    online_manager *_online_user;

    // 将房间id和管理指针进行绑定
    std::unordered_map<uint64_t, room_ptr> _rooms;

    // 将用户id和房间id进行绑定
    std::unordered_map<uint64_t, uint64_t> _users;

public:
    // 初始化房间ID计数器
    room_manager(user_table *ut, online_manager *om) : _tb_user(ut), _online_user(om)
    {
        LOG_INF("房间管理模块初始化完毕");
    }
    ~room_manager()
    {
        LOG_INF("房间管理模块销毁");
    }

    // 根据用户id创建房间
    room_ptr create_room(uint64_t uid1, uint64_t uid2)
    {
        // 两个用户在游戏大厅中进行对战匹配，匹配后创建房间

        LOG_INF("创建游戏房间的两个uid:%lu %lu",uid1,uid2);
        
        // 校验两个用户是否都在游戏大厅
        if (_online_user->is_in_game_hall(uid1) == false)
        {
            LOG_INF("%lu 用户不在游戏大厅", uid1);
            return room_ptr();
        }

        if (_online_user->is_in_game_hall(uid2) == false)
        {
            LOG_INF("%lu 用户不在游戏大厅", uid2);
            return room_ptr();
        }

        // 创建房间，将用户信息添加到房间中

        std::unique_lock<std::mutex> lock(_mutex);
        room_ptr rm(new room(_next_rid, _tb_user, _online_user));

        rm->add_white_user(uid1);
        rm->add_black_user(uid2);

        // 将房间信息管理起来
        _rooms.insert(std::make_pair(_next_rid, rm));

        _users.insert(std::make_pair(uid1, _next_rid));
        _users.insert(std::make_pair(uid2, _next_rid));
        _next_rid++;

        // 返回房间信息
        return rm;
    }

    // 获取房间
    room_ptr get_room_by_rid(uint64_t rid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _rooms.find(rid);
        if (it == _rooms.end())
        {
            return room_ptr();
        }

        return it->second;
    }

    room_ptr get_room_by_uid(uint64_t uid)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        auto it = _users.find(uid);
        if (it == _users.end())
        {
            return room_ptr();
        }

        auto rt = _rooms.find(it->second);

        if (rt == _rooms.end())
        {
            return room_ptr();
        }

        return rt->second;
    }

    // 销毁房间
    void remove_room(uint64_t rid)
    {
        
        room_ptr rp=get_room_by_rid(rid);
        
        if(rp.get()==nullptr)
        {
            return;
        }

        //销毁房间前，需要先移除房间中管理的用户信息
        uint64_t uid1=rp->get_white_user();
        uint64_t uid2=rp->get_black_user();

        //移除房间中管理的用户
        std::unique_lock<std::mutex> lock(_mutex);

        _users.erase(uid1);
        _users.erase(uid2);

        //移除房间信息
        _rooms.erase(rid);

        return;
    }

    // 删除房间中的用户
    void remove_room_user(uint64_t uid)
    {
        room_ptr rm=get_room_by_uid(uid);
        if(rm.get()==nullptr)
        {
            return;
        }

        //处理玩家退出动作
        rm->handle_exit(uid);

        //房间中若没有玩家则销毁房间

        if(rm->player_count()==0)
        {
            remove_room(rm->id());
        }
        return;
    }

};